/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.formserver.vmmanager.service;

import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.formserver.rt.api.ViewModelRtService;
import com.inspur.edp.formserver.rt.api.dto.ColumnSecurityFieldInfo;
import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.GspViewObject;
import com.inspur.edp.formserver.viewmodel.collection.VMElementCollection;
import com.inspur.edp.formserver.viewmodel.collection.ViewObjectCollection;
import com.inspur.edp.formserver.viewmodel.exception.ViewModelException;
import com.inspur.edp.formserver.vmmanager.exception.VoManagerErrorCodes;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.metadata.rtcustomization.api.CustomizationService;
import com.inspur.edp.metadata.rtcustomization.api.entity.MetadataQueryParam;
import com.inspur.edp.metadata.rtcustomization.api.entity.MetadataScopeEnum;
import io.iec.edp.caf.commons.utils.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nullable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 视图对象服务实现类
 */
@Slf4j
public class ViewModelRtServiceImpl implements ViewModelRtService {
    private final CustomizationService customizationService;

    public ViewModelRtServiceImpl(CustomizationService customizationService) {
        this.customizationService = customizationService;
    }

    @Override
    public Map<String, ColumnSecurityFieldInfo> getViewModelColumnSecurityFieldInfo(String viewModelId, List<String> fieldIds) {
        // 参数不合法时返回空集合
        if (StringUtils.isBlank(viewModelId) || CollectionUtils.isEmpty(fieldIds)) {
            return Collections.emptyMap();
        }
        // 获取主对象,若获取元数据存在异常，则返回空
        GspViewObject mainObject = getGspViewObject(viewModelId);
        // 如果主对象为空，则返回空集合
        if (mainObject == null) {
            log.debug("查询视图对象[{}]列权限时,视图对象不存在或其不存在主对象。", viewModelId);
            return Collections.emptyMap();
        }
        // 创建结果集
        Map<String, ColumnSecurityFieldInfo> result = new HashMap<>();

        // 遍历路径集合获取对应字段
        for (String fieldId : fieldIds) {
            // 分割路径节点
            String[] pathSegments = fieldId.split("#");
            // 跳过空路径
            if (pathSegments.length == 0) {
                log.debug("查询视图对象[{}]列权限时,存在fieldId分割后为空", viewModelId);
                continue;
            }

            // 逐级定位节点
            GspViewObject currentObject = mainObject;
            for (int i = 0; i < pathSegments.length - 1; i++) {
                // 逐级获取子节点
                currentObject = findChildByCode(currentObject.getContainChildObjects(), pathSegments[i]);
                // 如果子节点为空，则跳出循环
                if (currentObject == null) {
                    log.debug("查询视图对象[{}]列权限时,fieldid[{}]中指定的节点编码[{}]不存在", viewModelId, fieldId, pathSegments[i]);
                    break;
                }
            }
            // 如果子节点为空，则跳过当前fieldId
            if (currentObject == null) {
                log.debug("查询视图对象[{}]列权限时,fieldid[{}]中指定的节点路径不存在", viewModelId, fieldId);
                continue;
            }

            // 定位字段
            IGspCommonField field = findFieldByCode(currentObject.getContainElements(),
                    pathSegments[pathSegments.length - 1]);
            if (field == null) {
                log.debug("查询视图对象[{}]列权限时,fieldid[{}]未在指定节点路径下找到对应编码[{}]的字段", viewModelId, fieldId, pathSegments[pathSegments.length - 1]);
                continue;
            }

            // 组装结果
            ColumnSecurityFieldInfo info = new ColumnSecurityFieldInfo();
            info.setId(field.getID());
            info.setCode(field.getCode());
            info.setName(field.getName());
            result.put(fieldId, info);
        }
        return result;
    }

    /**
     * 根据视图模型ID获取主对象
     * @param viewModelId 视图模型ID
     * @return 主对象
     */
    @Nullable
    private GspViewObject getGspViewObject(String viewModelId) {
        // 获取元数据
        GspMetadata gspMetadata = customizationService.getGspMetadata(new MetadataQueryParam(viewModelId, true, MetadataScopeEnum.RUNTIME));
        // 如果元数据不存在, 则返回null
        if (gspMetadata == null) {
            return null;
        }
        // 如果元数据不是视图模型元数据，则抛出异常
        if (!(gspMetadata.getContent() instanceof GspViewModel)) {
            throw new ViewModelException(VoManagerErrorCodes.GSP_VIEWOBJECT_MANAGER_0094, null, viewModelId);
        }
        // 获取视图模型元数据
        GspViewModel viewModel = (GspViewModel) gspMetadata.getContent();
        return viewModel.getMainObject();
    }

    /**
     * 在子节点集合中查找指定编码的节点
     * @param children 子节点集合
     * @param code 要查找的编码
     */
    private GspViewObject findChildByCode(ViewObjectCollection children, String code) {
        if (children == null || StringUtils.isBlank(code)) {
            return null;
        }
        for (IGspCommonObject obj : children) {
            if (obj instanceof GspViewObject && code.equals(obj.getCode())) {
                return (GspViewObject) obj;
            }
        }
        return null;
    }

    /**
     * 在字段集合中查找指定编码的字段
     * @param elements 字段集合
     * @param code 要查找的编码
     * @return 字段
     */
    private IGspCommonField findFieldByCode(VMElementCollection elements, String code) {
        if (elements == null || StringUtils.isBlank(code)) {
            return null;
        }
        for (IGspCommonField field : elements) {
            if (code.equals(field.getCode())) {
                return field;
            }
        }
        return null;
    }
}
